#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/module.h>

#define LED_DRV_MAJOR 200
#define LED_DRV_NAME "led_drv"

//  寄存器声明
#define CCM_CCGR1_BASE (0x020C406C)         // 时钟配置
#define SW_MUX_GPIO1_IO03_BASE (0x020E0068) // 复用功能配置
#define SW_PAD_GPIO1_IO03_BASE (0x020E02F4) // 电气属性配置
#define GPIO1_DR_BASE (0x0209C000)          // 输出电平配置
#define GPIO1_GDIR_BASE (0x0209C004)        // 输出方向配置

// 转换后的地址
static void __iomem *CCM_CCGR1;
static void __iomem *SW_MUX_GPIO1_IO03;
static void __iomem *SW_PAD_GPIO1_IO03;
static void __iomem *GPIO1_DR;
static void __iomem *GPIO1_GDIR;

ssize_t led_drv_write(struct file *filp, const char __user *buf, size_t size, loff_t *pos)
{
    int ret, val;
    unsigned char mybuf[1];
    ret = copy_from_user(mybuf, buf, size);
    if (ret > 0)
    {
        printk("copy from user error\r\n");
        return -1;
    }

    if (mybuf[0] == '0')
    {
        printk("led close\r\n");
        // 输出高电平关闭灯
        val = readl(GPIO1_DR);
        val |= (1 << 3);
        writel(val, GPIO1_DR);
    }
    else if (mybuf[0] == '1')
    {
        printk("led open\r\n");
        // 输出低电平打开灯
        val = readl(GPIO1_DR);
        val &= ~(1 << 3);
        writel(val, GPIO1_DR);
    }
    else
    {
        printk("cmd is error!\r\n");
    }
    return 0;
}

int led_drv_open(struct inode *inode, struct file *filp)
{
    printk("led_drv open!\r\n");
    return 0;
}
int led_drv_close(struct inode *inode, struct file *filp)
{
    printk("led_drv close!\r\n");
    return 0;
}

static const struct file_operations led_fops = {
    .owner = THIS_MODULE,
    .open = led_drv_open,
    .release = led_drv_close,
    .write = led_drv_write,
};

// 字符设备操作集
static int __init led_drv_init(void)
{
    int ret;
    int val;

    printk("led_drv module init\r\n");
    // 注册设备号
    ret = register_chrdev(LED_DRV_MAJOR, LED_DRV_NAME, &led_fops);
    if (ret < 0)
    {
        printk("register chrdev error\r\n");
        return EIO;
    }

    // 初始化LED相关寄存器
    CCM_CCGR1 = ioremap(CCM_CCGR1_BASE, 4);
    SW_MUX_GPIO1_IO03 = ioremap(SW_MUX_GPIO1_IO03_BASE, 4);
    SW_PAD_GPIO1_IO03 = ioremap(SW_PAD_GPIO1_IO03_BASE, 4);
    GPIO1_DR = ioremap(GPIO1_DR_BASE, 4);
    GPIO1_GDIR = ioremap(GPIO1_GDIR_BASE, 4);

    // 设置时钟
    val = readl(CCM_CCGR1);
    val &= ~(3 << 26);
    val |= 3 << 26;
    writel(val, CCM_CCGR1);

    // 设置IO复用
    writel(0x5, SW_MUX_GPIO1_IO03);
    // 设置电气属性
    writel(0x10B0, SW_PAD_GPIO1_IO03);

    // 设置输出
    val = readl(GPIO1_GDIR);
    val |= 1 << 3;
    writel(val, GPIO1_GDIR);

    // 输出高电平关闭灯
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);

    return 0;
}

static void __exit led_drv_exit(void)
{
    // 输出高电平关闭灯
    int val;
    val = readl(GPIO1_DR);
    val |= (1 << 3);
    writel(val, GPIO1_DR);
    // 解除映射
    iounmap(CCM_CCGR1);
    iounmap(SW_MUX_GPIO1_IO03);
    iounmap(SW_PAD_GPIO1_IO03);
    iounmap(GPIO1_DR);
    iounmap(GPIO1_GDIR);

    // 注销设备号
    unregister_chrdev(LED_DRV_MAJOR, LED_DRV_NAME);
    printk("led_drv module exit\r\n");
}

// 模块的注册和卸载
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("YAO SHEN SHUO");